package dongchel.vrp.entity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import dongchel.vrp.clusterandroute.Cluster;
import dongchel.vrp.general.SimpleComparator;
import dongchel.vrp.general.SimpleCompareObject;
import dongchel.vrp.general.vrpinterface.ObjectivePenaltyInterface;
import dongchel.vrp.rachelinsertion.Insertion;

public class Solution implements Cloneable {
	/**
	 * (1)routeList (2)unvisited Cities solutionLength solutionDuration
	 * 
	 */
	private int id;

	private List<Route> routeList;
	private List<City> unvisitedCities;

	private double solutionLength;
	private double solutionDuration;

	private ObjectivePenaltyInterface objectivePenalty;
	private Insertion insertion;

	/**
	 * objective value in the last iteration
	 * @param objectivePenalty
	 * @param insertion
	 */
	private MultiObjectiveObject lastObjective = null;
	private MultiObjectiveObject lastPenalty = null;

	public Solution(ObjectivePenaltyInterface objectivePenalty,
			Insertion insertion) {
		this.objectivePenalty = objectivePenalty;
		this.insertion = insertion;
		this.unvisitedCities = new ArrayList<City>();
		this.routeList = new ArrayList<Route>();
		this.setId(GlobalInformation.getSolutionNum());
		for (int i = 1; i < GlobalInformation.cityList.size(); i++) {
			this.unvisitedCities.add(GlobalInformation.cityList.get(i).clone());
		}
	}

	/**
	 * addRoute removeRoute
	 * 
	 * checkTimeFeasibility checkDemandFeasibility
	 * 
	 * @return
	 */
	public MultiObjectiveObject getObjectiveValue() {
		return this.objectivePenalty.getObjectiveValue(this);
	}

	public MultiObjectiveObject getPenaltyValue() {
		return this.objectivePenalty.getPaneltyValue(this);
	}

	public void addRoute(Route route) {
		this.getRouteList().add(route);

	};

	public void removeRoute(Route route) {
		this.getRouteList().remove(route);
	};

	public boolean checkTimeFeasibility() {
		for (int i = 0; i < routeList.size(); i++) {
			if (!routeList.get(i).isTimeFeasibility()) {
				return false;
			}
		}
		return true;
	}

	public boolean checkDemandFeasibility() {
		for (int i = 0; i < routeList.size(); i++) {
			if (!routeList.get(i).isDemandFeasibility()) {
				return false;
			}
		}
		return true;
	}

	public List<Route> getRouteList() {
		return routeList;
	}

	public void setRouteList(List<Route> routeList) {
		this.routeList = routeList;
	}

	public List<City> getUnvisitedCities() {
		return unvisitedCities;
	}

	public void setUnvisitedCities(List<City> unvisitedCities) {
		this.unvisitedCities = unvisitedCities;
	}

	public double getSolutionLength() {
		double totalLength = 0;
		for (int i = 0; i < this.routeList.size(); i++) {
			totalLength += this.routeList.get(i).getTotalLength();
		}
		return totalLength;
	}

	public void setSolutionLength(double solutionLength) {
		this.solutionLength = solutionLength;
	}

	public double getSolutionDuration() {
		return solutionDuration;
	}

	public void setSolutionDuration(double solutionDuration) {
		this.solutionDuration = solutionDuration;
	}

	public ObjectivePenaltyInterface getObjectivePenalty() {
		return objectivePenalty;
	}

	public void setObjectivePanelty(ObjectivePenaltyInterface objectivePenalty) {
		this.objectivePenalty = objectivePenalty;
	}

	public Insertion getInsertion() {
		return insertion;
	}

	public void setInsertion(Insertion insertion) {
		this.insertion = insertion;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	/**
	 * remove city from unvisited city list
	 */
	public void removeCityFromUnvisited(City city) {
		int index = unvisitedCities.indexOf(city);
		if (index >= 0) {
			unvisitedCities.remove(index);
		}
	}

	public Object clone() {
		Solution solution = null;
		try {
			solution = (Solution) super.clone();
			List<Route> newSolution = new ArrayList<Route>();
			for (int i = 0; i < solution.getRouteList().size(); i++) {
				Route r = (Route) solution.getRouteList().get(i).clone();
				r.setSolution(solution);
				newSolution.add(r);
			}
			solution.setRouteList(newSolution);

		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return solution;
	}

	/**
	 * sort unvisited cities by angle
	 */
	public void sortUnvisitedCitesByAngle() {

		List<SimpleCompareObject> scList = new ArrayList<SimpleCompareObject>();

		for (int i = 0; i < unvisitedCities.size(); i++) {
			SimpleCompareObject sc = new SimpleCompareObject();
			sc.setPosition(i);
			sc.setValue(unvisitedCities.get(i).getAngle());
			scList.add(sc);
		}

		Collections.sort(scList, new SimpleComparator());
		List<City> sortedCityList = new ArrayList<City>();
		for (int i = 0; i < scList.size(); i++) {
			sortedCityList
					.add(unvisitedCities.get(scList.get(i).getPosition()));
		}

		unvisitedCities = sortedCityList;

	}

	public void reverseUnvisitedCityList() {
		Collections.reverse(unvisitedCities);
	}

	/**
	 * generate clusters given a certain seed
	 */
	public List<Cluster> generateClusters(int initPosition) {
		List<Cluster> clusterList = new ArrayList<Cluster>();
		
		int type = unvisitedCities.get(0).getType();
		if (type == 0) {
			double totalCapacity = 0;
			Cluster cluster = new Cluster(this);
			for (int i = initPosition; i < unvisitedCities.size()
					+ initPosition; i++) {
				City city = null;
				if (i < unvisitedCities.size()) {
					city = unvisitedCities.get(i);
				} else {
					city = unvisitedCities.get(i - unvisitedCities.size());
				}
				if (totalCapacity + city.getDemand() > GlobalInformation.vehicleCapacity) {
					clusterList.add(cluster);
					cluster = new Cluster(this);
					totalCapacity = city.getDemand();
					cluster.getCityList().add(city);
				} else {
					totalCapacity += city.getDemand();
					cluster.getCityList().add(city);
				}
			}
			clusterList.add(cluster);
		} else {
			double totalDeliveryCapacity = 0;
			double totalPickupCapacity = 0;
			Cluster cluster = new Cluster(this);
			for (int i = initPosition; i < unvisitedCities.size()
			+ initPosition; i++) {
				City city = null;
				if (i < unvisitedCities.size()) {
					city = unvisitedCities.get(i);
				} else {
					city = unvisitedCities.get(i - unvisitedCities.size());
				}
				if (totalDeliveryCapacity + city.getDeliveryDemand() > GlobalInformation.vehicleCapacity
						|| totalPickupCapacity + city.getPickupDemand() > GlobalInformation.vehicleCapacity) {
					clusterList.add(cluster);
					cluster = new Cluster(this);
					totalDeliveryCapacity = city.getDeliveryDemand();
					totalPickupCapacity = city.getPickupDemand();
					cluster.getCityList().add(city);
				} else {
					totalDeliveryCapacity += city.getDeliveryDemand();
					totalPickupCapacity += city.getPickupDemand();
					cluster.getCityList().add(city);
				}
			}
			clusterList.add(cluster);
		}

		return clusterList;
	}
	
	public double getTimeViolation(){
		double totalViolation = 0;
		for(int i=0;i<this.routeList.size();i++){
			totalViolation += this.routeList.get(i).getTimeViolation();
		}
		return totalViolation;
	}
	
	public double getCapacityViolation(){
		double totalViolation = 0;
		for(int i=0;i<this.routeList.size();i++){
			totalViolation += this.routeList.get(i).getCapacityViolation();
		}
		return totalViolation;
	}
	
	public void removeRouteById(int id){
		for(int i=0;i<this.routeList.size();i++){
			System.out.println("id = "+id);
			System.out.println("this id = "+this.routeList.get(i).getId());
			if(this.routeList.get(i).getId()==id){
				System.out.println("found route id"+id);
				this.routeList.remove(i);
				break;
			}
		}	
	}
	
	public MultiObjectiveObject getLastObjective() {
		return lastObjective;
	}

	public void setLastObjective(MultiObjectiveObject lastObjective) {
		this.lastObjective = lastObjective;
	}

	public MultiObjectiveObject getLastPenalty() {
		return lastPenalty;
	}

	public void setLastPenalty(MultiObjectiveObject lastPenalty) {
		this.lastPenalty = lastPenalty;
	}
	
	public double getObjectiveSpeed(double[] weights){
		if(lastObjective == null){
			return 0;
		}else{
			return this.getObjectiveValue().minus(this.lastObjective).getRandomSummation(weights);
		}
	}
	
	public double getPenaltySpeed(double[] weights){
		if(lastPenalty == null){
			return 0;
		}else{
			return this.getPenaltyValue().minus(this.lastPenalty).getRandomSummation(weights);
		}
	}
	
	public String toString(){
		return String.valueOf(this.getId());
	}
}
